package ar.edu.utn.frba.dds.opendesigners.escalas;

import java.util.ArrayList;
import java.util.List;

import ar.edu.utn.frba.dds.opendesigners.ParametroBusqueda;
import ar.edu.utn.frba.dds.opendesigners.aerolineas.AerolineaAdapter;
import ar.edu.utn.frba.dds.opendesigners.resultados.ResultadoBusqueda;
import ar.edu.utn.frba.dds.opendesigners.resultados.Asiento;
import ar.edu.utn.frba.dds.opendesigners.resultados.VueloConEscalas;
import ar.edu.utn.frba.dds.opendesigners.resultados.VueloDirecto;

public class Raiz {

	private ParametroBusqueda parametros;
	private AerolineaAdapter aerolinea;
	private ArrayList<Rama> destinos;	//Lista de otras "ramas" con vuelos que salen de donde llega este
	private Integer maxEscalas;

	/* Si "reversa" es verdadero, empieza buscando desde el destino. Esto implica que hay que invertir las comprobaciones
	 * y verificar, no que la fecha de salida de los "destinos" sea posterior a la fecha de llegada, 
	 * sino que la fecha de llegada de los "destinos" sea anterior a la fecha de salida.  
	*/
	private Boolean reversa; 			
	
	public Raiz(ParametroBusqueda _param, AerolineaAdapter _aerolinea, Boolean _reversa, Integer _cantidadEscalas){
		
		this.aerolinea = _aerolinea;
		this.reversa = _reversa;
		this.parametros = _param;
		this.maxEscalas = _cantidadEscalas;
		this.destinos = new ArrayList<Rama>();
		
		ResultadoBusqueda listaInicial;
		//Me piden buscar desde el destino..
		if (reversa){
			//Armo un parametro de busqueda con origen pero sin destino.
	    	ParametroBusqueda paramTemp = this.parametros.copia();
	    	paramTemp.setOrigen(null);
	    	
	    	listaInicial = this.aerolinea.buscar(paramTemp);
		}
		else{
			//Armo un parametro de busqueda con origen pero sin destino.
	    	ParametroBusqueda paramTemp = this.parametros.copia();
	    	paramTemp.setDestino(null);
	    	
	    	listaInicial = this.aerolinea.buscar(paramTemp);
		}
		
		this.cargarListaTramos(listaInicial);
		
		
	}
	
	public ResultadoBusqueda buscarTramos() {
		ResultadoBusqueda resultado = new ResultadoBusqueda();
		
		//Configuro que todos pueden tener como mucho esta cantidad de escalas
		
		ParametroBusqueda paramTemp = this.parametros.copia();
		
		//Para cada "rama" voy buscando y agregando otros destinos
		for (Rama rama : this.destinos){
			rama.agregarTramos(paramTemp, aerolinea,  this.maxEscalas, reversa);
		}
		
		/*
		 * Una vez que tengo el arbol entero, tengo que recorrerlo, filtrando todos los caminos que no llevan a 
		 * ningun lugar, y traer las combinaciones de vuelos que me lleven al destino según lo planeado
		 */
		resultado = abrirArbol();
		
		return resultado;
	}
	
	private void agregarTramoDestino(VueloDirecto _vuelo){
		Rama nuevaRama = new Rama(_vuelo);
		this.destinos.add(nuevaRama);
	}
	
	private void cargarListaTramos(ResultadoBusqueda lista){
		for (Asiento vuelo : lista.getListaResultados()){
			//Para evitar duplicados, solo cargo aquellos que no sean vuelos directos (esos los trajo la busqueda directa que se corre antes)
			if (!this.parametros.getOrigen().equals(vuelo.getOrigen()) || !this.parametros.getDestino().equals(vuelo.getDestino())){
				//Luego, si estoy buscando de atras para adelante, no me sirven los vuelos que lleguen a un lugar que no sea el destino
				if (reversa){
					if (this.parametros.getDestino().equals(vuelo.getDestino())){
						this.agregarTramoDestino((VueloDirecto) vuelo);
					}
				}else{
					if (this.parametros.getOrigen().equals(vuelo.getOrigen())){
						//Mismo aca. La raiz solo debe ser comenzando desde el origen
						this.agregarTramoDestino((VueloDirecto) vuelo);
					}
				}
				
			}
		}
	}

	private ResultadoBusqueda abrirArbol(){
		ResultadoBusqueda resultado = new ResultadoBusqueda();
		List<List<VueloDirecto>> vuelosRama; //Esto contiene lo que me devueve cada rama
		for (Rama ramita : this.destinos){
			//... que me devuelva su lista
			vuelosRama = ramita.desmenuzar();
			
			for (List<VueloDirecto> listaVuelosItinerario : vuelosRama){
				VueloConEscalas itinerarioCompleto = new VueloConEscalas();
				for (VueloDirecto vuelito : listaVuelosItinerario){
					itinerarioCompleto.agregarTramo(vuelito);
				}
				
				//Si se busco desde destino, lo invierto asi tengo el itinerario saliendo desde origen
				if (this.reversa){
					itinerarioCompleto.InvertirItinerario();
				}
				
				//Ahora me fijo si el itinerario sale del origen y llega al destino buscado
				if (esItinerarioValido(itinerarioCompleto)){
					resultado.agregarItem(itinerarioCompleto);
				}
			}
			
		}
		
		return resultado;
	}
	
	private Boolean esItinerarioValido(VueloConEscalas itinerario){
		
		if (itinerario.getOrigen().equalsIgnoreCase(this.parametros.getOrigen()) && 
				itinerario.getDestino().equalsIgnoreCase(this.parametros.getDestino())){
			return true;
		}
		else{
			return false;
		}
			
		
	}
}
